package com.epoint.nio.socket3_thread;

import com.epoint.nio.NioUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ServerPro {
    public static void main(String[] args) throws Exception {
        try(
            // 2.创建服务器
            ServerSocketChannel server = ServerSocketChannel.open()
        ){
            Thread.currentThread().setName("main线程~");

            // 2.1绑定监听端口
            server.bind(new InetSocketAddress(8080));
            // 2.2设置非阻塞
            server.configureBlocking(false);

            // 创建Select
            Selector selector = Selector.open();
            server.register(selector, SelectionKey.OP_ACCEPT);

            Worker worker = new Worker();

            /**
             * 4.一直循环接受客户端
             *
             */
            while (true){
                selector.select();

                // 获取所有事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();

                // 因为下面会涉及到循环里进行操作，故这里直接获取迭代器
                Iterator<SelectionKey> iterator = selectionKeys.iterator();

                while (iterator.hasNext()){
                    // 事件
                    SelectionKey key = iterator.next();

                    // 根据事件类型进行判断
                    if(key.isAcceptable()){
                        // 获取对应的通道
                        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                        // 客户端
                        SocketChannel accept = channel.accept();
                        if(accept != null){
                            System.out.println("有一个客户端连接" + accept);
                            // 设置非阻塞
                            accept.configureBlocking(false);

                            worker.register(accept);
                        }
                    }
                    iterator.remove();
                }
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 另外个线程，用于 read、write
     */
    static class Worker extends Thread{

        private volatile boolean isRegister;

        private Selector selector;

        private final ConcurrentLinkedQueue<Runnable> tasks = new ConcurrentLinkedQueue<>();

        public void register(SocketChannel sc) throws IOException {
            if(!isRegister){
                this.selector = Selector.open();
                new Thread(this).start();

                // 添加事件的runnable
                tasks.add(()->{
                    try {
                        System.out.println("workor注册：" + sc);
                        sc.register(selector, SelectionKey.OP_READ);
                    } catch (ClosedChannelException e) {
                        e.printStackTrace();
                    }
                });
                isRegister = true;
            }
        }

        @Override
        public void run() {
            Thread.currentThread().setName("work线程~");
            while (true){
                try{
                    // 再取出来后 注册一下read事件
                    Runnable poll = tasks.poll();
                    if(poll != null){
                        poll.run();
                    }

                    selector.select();
                    // 获取所有事件
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();

                    while (iterator.hasNext()){
                        // 事件
                        SelectionKey key = iterator.next();

                        if(key.isReadable()){

                            SocketChannel channel = (SocketChannel) key.channel();

                            // 1.开辟一个缓存buffer
                            ByteBuffer buffer = ByteBuffer.allocate(16);

                            // 如果这时候输入20个byte的数据会怎么样？会触发2次事件，直到把通道的数据走完才行
                            if(channel.read(buffer) > 0){
                                buffer.flip();

                                // 打印数据
                                System.out.println(NioUtil.getStr(buffer));

                                buffer.clear();
                            }
                        }
                        iterator.remove();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}
